package com.tian.intercptor;

import com.tian.annotation.HasPermission;
import com.tian.config.RedisCacheUtil;
import com.tian.dao.entity.Menu;
import com.tian.dto.resp.UserInfoRespDto;
import com.tian.enums.OptTypeEnum;
import com.tian.enums.ResultCode;
import com.tian.exception.BusinessException;
import com.tian.utils.UserCacheUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @author tianwc  公众号：java后端技术全栈、面试专栏
 * @version 1.0.0
 * 2022年11月01日 17:24
 * <p>
 * 权限校验切面
 * 1. 先从缓存中获取当前用户的角色菜单列表
 * 2. 从菜单列表中找到当前请求的权限标识符
 * 3. 判断权限标识符是否匹配
 * 4. 如果匹配，则放行，否则提示无权限
 */
@Aspect
@Component
@Slf4j
@Order(2)
public class PermissionCheckAspect {
    @Resource
    private RedisCacheUtil redisCacheUtil;

    /**
     * 这里我们使用注解的形式
     * 当然，我们也可以通过切点表达式直接指定需要拦截的package,需要拦截的class 以及 method
     * 切点表达式:   execution(...)
     * 接口请求参数、相应参数、耗时、路径等信息打印
     */
    @Around("@annotation(com.tian.annotation.HasPermission)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        HasPermission annotation = method.getAnnotation(HasPermission.class);
        String permissionFlagMethod = annotation.value();
        UserInfoRespDto user = UserCacheUtil.getUser();
        Integer roleId = user.getRoleId();
        List<Menu> menuList = redisCacheUtil.getCacheList(roleId.toString());
        if (CollectionUtils.isEmpty(menuList)) {
            throw new BusinessException(ResultCode.NO_ACCESS_PERMISSION);
        }
        boolean hasPermission = false;
        for (Menu menu : menuList) {
            if (!StringUtils.hasText(menu.getPermissionFlag())) {
                continue;
            }
            String permissionFlag = menu.getPermissionFlag() + ":" + OptTypeEnum.getEn(menu.getOptType());
            // 判断权限标识符是否匹配
            if (permissionFlag.equals(permissionFlagMethod)) {
                hasPermission = true;
                break;
            }
        }
        if (!hasPermission) {
            throw new BusinessException(ResultCode.NO_ACCESS_PERMISSION);
        }
        return joinPoint.proceed();
    }
}
